#include "spacetime.hpp"
#include "lines.hpp"
#include <cstdint>
#include <cstdio>

namespace Trade {

bool
Timer::is_time_outed() const noexcept
{
  return to;
}

bool
Timer::is_approximate_to() const noexcept
{
  return in_range;
}

bool
Timer::is_absulute_to() const noexcept
{
  return fto;
}

bool
Timer::time_out(const time_t t_now) noexcept
{
  if (t && !to) {
    to = ((t_now / 60) >= (t / 60));
    if (to)
      return true;
  }
  return false;
}

bool
Timer::approximately_time_out(const time_t t_now) noexcept
{
  if (t) {
    time_out(t_now);
    absulutely_timer_out(t_now);

    auto now = int(t_now / 60);
    auto far = int(t / 60);
    bool in = (now >= far - err && now <= far + err);
    if (in != in_range) {
      in_range = in;
      return in_range;
    }
  }
  return false;
}

bool
Timer::absulutely_timer_out(const time_t t_now) noexcept
{
  if (t && !fto) {
    auto now = int(t_now / 60);
    auto far = int(t / 60);
    fto = (now >= far + err);
    if (fto)
      return true;
  }
  return false;
}

void
Timer::set_timer(const time_t tm) noexcept
{
  to = fto = in_range = false;
  t = tm;
  err = 15;
}

void
Timer::set_timer(const time_t tm, const int err) noexcept
{
  to = fto = in_range = false;
  t = tm;
  this->err = err;
}

void
Spacetime::print(const char* title,
                 const int64_t idx,
                 const price_t hp_now,
                 const time_t t_now) noexcept
{
  const int8_t color = 004;
  printf("\033[38;5;%dm ---==>> %s, %ld mins <<==---\033[0m ",
         color,
         title,
         (timer.t - t_now) / 60);
  lines.print(idx, hp_now);
  char buf[128];
  snprintf(buf, 128, "\033[38;5;%dm %s Reached: \033[0m", color, title);
  print_reached(buf, 0, reached, idx);
}

void
Spacetime::set_timer(const time_t tm, const time_t tm2) noexcept
{
  timer.set_timer(tm);
  timer_2.set_timer(tm2);

  if (reached & LIMIT_REACHED::REACHED_TIMER_OUT)
    reached ^= LIMIT_REACHED::REACHED_TIMER_OUT;
}

void
Spacetime::set_lines(const Lines& l) noexcept
{
  lines.copy(l);
}

bool
Spacetime::update_lines(const dir_t d,
                        const Lines& l,
                        const price_t hp_now) noexcept
{
  bool updated = false;
  if (lines.good_main_space()) {
    if (lines.amount(d)) {
      updated = lines.update_lines(l, d, hp_now);
    } else {
      lines.copy(l);
      updated = true;
    }
    reached |= lines.reached;
  } else if (l.good_main_space()) {
    lines.copy(l);
    updated = true;
  }
  return updated;
}

uint8_t
Spacetime::reached_line_and_timer(const price_t hp_now,
                                  const time_t t_now) noexcept
{
  uint8_t rchd = lines.limit_reached(hp_now);
  if (timer.time_out(t_now)) {
    rchd |= LIMIT_REACHED::REACHED_TIMER_OUT;
  }
  if (rchd) {
    if (reached != (reached | rchd)) {
      reached |= rchd;
      return rchd;
    }
  }
  return (0);
}

uint8_t
Spacetime::check_reached(const time_t t_now,
                         const price_t hp_now,
                         Lines& l_now [[maybe_unused]],
                         const int64_t idx) noexcept
{
  uint8_t rchd = lines.limit_reached(hp_now);
  // dir_t er_dir = l_now.ext_rgn.ext_dir(hp_now);
  // if (er_dir < 0 && l_now.ext_rgn.top && !l_now.ext_rgn.btm) {
  //   price_t er_top =
  //     l_now.ext_rgn.hpx - l_now.ext_rgn.hp_A; // hp_top = hpx - top;
  //   rchd |= lines.limit_reached(er_top);
  // } else if (er_dir > 0 && l_now.ext_rgn.top && l_now.ext_rgn.btm) {
  //   price_t er_btm =
  //     l_now.ext_rgn.hpx - l_now.ext_rgn.hp_V; // hp_btm = hpx - btm
  //   rchd |= lines.limit_reached(er_btm);
  // }

  uint8_t r = 0;

  dir_t er_dir = lines.ext_rgn.ext_dir(hp_now);
  if (er_dir < 0 && lines.ext_rgn.top && !lines.ext_rgn.btm) {
    price_t er_top =
      lines.ext_rgn.hpx - lines.ext_rgn.hp_A; // hp_top = hpx - top;
    rchd |= lines.limit_reached(er_top);
  } else if (er_dir > 0 && lines.ext_rgn.top && lines.ext_rgn.btm) {
    price_t er_btm =
      lines.ext_rgn.hpx - lines.ext_rgn.hp_V; // hp_btm = hpx - btm
    rchd |= lines.limit_reached(er_btm);
  }

  if (timer.time_out(t_now)) {
    rchd |= LIMIT_REACHED::REACHED_TIMER_OUT;
  }

  if (rchd) {
    if (!(reached & LIMIT_REACHED::REACHED_TIMER_OUT) &&
        (rchd & LIMIT_REACHED::REACHED_TIMER_OUT)) {
      r |= LIMIT_REACHED::REACHED_TIMER_OUT;
    }
    if (!(reached & LIMIT_REACHED::REACHED_UP_1) && (rchd & LIMIT_REACHED::REACHED_UP_1)) {
      r |= LIMIT_REACHED::REACHED_UP_1;
    }
    if (!(reached & LIMIT_REACHED::REACHED_UP_2) && (rchd & LIMIT_REACHED::REACHED_UP_2)) {
      r |= LIMIT_REACHED::REACHED_UP_2;
    }
    if (!(reached & LIMIT_REACHED::REACHED_DN_1) && (rchd & LIMIT_REACHED::REACHED_DN_1)) {
      r |= LIMIT_REACHED::REACHED_DN_1;
    }
    if (!(reached & LIMIT_REACHED::REACHED_DN_2) && (rchd & LIMIT_REACHED::REACHED_DN_2)) {
      r |= LIMIT_REACHED::REACHED_DN_2;
    }

    reached |= rchd;
  }

  return r;
}

bool
Spacetime::timer_outed() noexcept
{
  return (reached & LIMIT_REACHED::REACHED_TIMER_OUT);
}

bool
Spacetime::second_timer_outed(const time_t tm) noexcept
{
  return ((timer.t - tm) < 900);
}

void
Spacetime::clear_reacheded() noexcept
{
  reached = 0;
  lines.reached = 0;
  timer.to = 0;
}

uint8_t
Spacetime::reacheded() const noexcept
{
  return reached;
}

void
Spacetime::update_timer(const time_t tm, const time_t tm2) noexcept
{
  timer.set_timer(tm);
  if (reached & LIMIT_REACHED::REACHED_TIMER_OUT)
    reached ^= LIMIT_REACHED::REACHED_TIMER_OUT;
  if (tm2) {
    timer_2.set_timer(tm2);
  }
}

void
Spacetime::erase_opposite_reached(const dir_t d, const uint8_t rchd) noexcept
{
  if (d > 0 && (reached & (LIMIT_REACHED::REACHED_DN_1 | LIMIT_REACHED::REACHED_DN_2)) &&
      (rchd & (LIMIT_REACHED::REACHED_UP_1 | LIMIT_REACHED::REACHED_UP_2))) {
    if (reached & LIMIT_REACHED::REACHED_DN_1)
      reached ^= LIMIT_REACHED::REACHED_DN_1;
    if (reached & LIMIT_REACHED::REACHED_DN_2)
      reached ^= LIMIT_REACHED::REACHED_DN_2;
  } else if (d < 0 && (reached & (LIMIT_REACHED::REACHED_UP_1 | LIMIT_REACHED::REACHED_UP_2)) &&
             (rchd & (LIMIT_REACHED::REACHED_DN_1 | LIMIT_REACHED::REACHED_DN_2))) {
    if (reached & LIMIT_REACHED::REACHED_UP_1)
      reached ^= LIMIT_REACHED::REACHED_UP_1;
    if (reached & LIMIT_REACHED::REACHED_UP_2)
      reached ^= LIMIT_REACHED::REACHED_UP_2;
  }
}

} // namespace Trade